// Copyright 2022 jeff.li. and/or its affiliates.

#pragma once

#include <cstddef>
#include <functional>
#include <memory>

#include <tbir/runtime/object.h>
#include <tbir/runtime/runtime_value.h>

namespace tbir::runtime {

    /******************************************************************************
     * Generic iterator, like python iter
     *****************************************************************************/
    class IteratorNode;

    class Iterator : public ObjectRef {
    public:
        using ContainerType = IteratorNode;
        static constexpr bool _type_is_nullable = false;  // disable nullptr for performance

    public:
        // operators
        bool HasNext() const;

        RTValue Next() const;

        RTValue Next(bool *has_next) const;

        RTView NextView(bool *has_next, RTValue *holder_or_null) const;

        int64_t Distance() const;

        Iterator() noexcept = default;

        // copy and assign
        Iterator(const Iterator &other) noexcept = default;

        Iterator(Iterator &&other) noexcept = default;

        Iterator &operator=(const Iterator &other) noexcept = default;

        Iterator &operator=(Iterator &&other) noexcept = default;

        explicit Iterator(ObjectPtr <Object> n) noexcept: ObjectRef(std::move(n)) {
        }

        // Node
        IteratorNode *GetMutableNode() const;

    public:
        static Iterator MakeGenericIterator(RTValue container,
                                            std::function<bool()> has_next,
                                            std::function<RTValue()> next,
                                            std::function<RTValue(bool * )> next_and_check);

        static Iterator MakeGenericIterator(const Any &container);

        static Iterator MakeItemsIterator(const Any &container);

        static bool all_items_equal(const Iterator &lhs, const Iterator &rhs);
    };

    namespace TypeIndex {
        template<>
        struct type_index_traits<Iterator> {
            static constexpr int32_t value = kRuntimeIterator;
        };
    }  // namespace TypeIndex

    template<>
    bool IsConvertible<Iterator>(const Object *node);

    // RTValue converter
    template<>
    TURBO_FORCE_INLINE Iterator Any::As<Iterator>() const {
        TBIR_RUNTIME_VALUE_CHECK_TYPE_CODE(value_.code, TypeIndex::kRuntimeIterator);
        return Iterator(GetObjectPtr<Object>(static_cast<Object *>(value_.data.v_handle)));
    }

    template<>
    TURBO_FORCE_INLINE Iterator Any::AsNoCheck<Iterator>() const {
        return Iterator(GetObjectPtr<Object>(static_cast<Object *>(value_.data.v_handle)));
    }

    std::ostream &operator<<(std::ostream &os, Iterator const &n);

}  // namespace tbir::runtime
